package vip.liux.front.infrastructure.annotations;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.autoproxy.AbstractBeanFactoryAwareAdvisingPostProcessor;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.lang.Nullable;
import org.springframework.scheduling.annotation.AnnotationAsyncExecutionInterceptor;
import org.springframework.util.Assert;
import org.springframework.util.function.SingletonSupplier;
import vip.liux.front.infrastructure.aop.interceptor.IAuditingStore;
import vip.liux.front.infrastructure.utils.AmbientScopeProvider;

import java.lang.annotation.Annotation;
import java.util.function.Supplier;

@SuppressWarnings("serial")
public class AuditedAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {

    public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME =
            AnnotationAsyncExecutionInterceptor.DEFAULT_TASK_EXECUTOR_BEAN_NAME;

    protected final Log logger = LogFactory.getLog(getClass());

    @Nullable
    private Supplier<IAuditingStore> auditingStore;

    @Nullable
    private Supplier<AmbientScopeProvider<?>> ambientScopeProvider;

    @Nullable
    private Class<? extends Annotation> auditedAnnotationType;

    public AuditedAnnotationBeanPostProcessor() {
        setBeforeExistingAdvisors(true);
    }

    public void configure(@Nullable Supplier<IAuditingStore> executor,
                          @Nullable Supplier<AmbientScopeProvider<?>> exceptionHandler) {

        this.auditingStore = executor;
        this.ambientScopeProvider = exceptionHandler;
    }

    public void setAuditingStore(IAuditingStore auditingStore) {
        this.auditingStore = SingletonSupplier.of(auditingStore);
    }


    public void setAmbientScopeProvider(AmbientScopeProvider<?> ambientScopeProvider) {
        this.ambientScopeProvider = SingletonSupplier.of(ambientScopeProvider);
    }

    public void setAuditedAnnotationType(Class<? extends Annotation> auditedAnnotationType) {
        Assert.notNull(auditedAnnotationType, "'auditedAnnotationType' must not be null");
        this.auditedAnnotationType = auditedAnnotationType;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        super.setBeanFactory(beanFactory);

        AuditedAnnotationAdvisor advisor = new AuditedAnnotationAdvisor(this.auditingStore, this.ambientScopeProvider);
        if (this.auditedAnnotationType != null) {
            advisor.setAuditedAnnotationType(this.auditedAnnotationType);
        }
        advisor.setBeanFactory(beanFactory);
        this.advisor = advisor;
    }
}
